package per.zzzfwd.douban.crawler;

import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;
import org.jsoup.nodes.TextNode;
import org.jsoup.select.Elements;
import per.zzzfwd.douban.crawler.model.Movie;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Main {

    private static final Crawler crawler = new Crawler(5000L);
    private static final Extractor extractor = new Extractor();
    private static final Parser parser = new Parser();

    public static void main(String[] args) {
//        top250();
//        newFilmChart();
//        weekMovie();
        usWeekMovie();
    }

    /**
     * 豆瓣TOP250电影
     */
    public static void top250() {
        List<Movie> movies = Stream.iterate(0, i -> i + 25).limit(250)
            .map(i ->
                getMoviesByUrl(
                    "https://movie.douban.com/top250?start=" + i,
                    document -> document.body().select(".item")
                )
            )
            .flatMap(Collection::stream)
            .collect(Collectors.toList());
        // 保存到excel
        saveExcel("douban-movie-top250", movies);
    }

    /**
     * 豆瓣新片榜
     */
    public static void newFilmChart() {
        List<Movie> movies = getMoviesByUrl(
            "https://movie.douban.com/chart",
            document -> document.body().select(".item")
        );
        saveExcel("douban-new-film", movies);
    }

    /**
     * 一周口碑榜
     */
    public static void weekMovie() {
        List<Movie> movies = getMoviesByUrl(
            "https://movie.douban.com/chart",
            document -> document.body().select(".name")
        );
        saveExcel("douban-week-movie", movies);
    }

    /**
     * 北美票房榜
     */
    public static void usWeekMovie() {
        List<Movie> movies = getMoviesByUrl(
            "https://movie.douban.com/chart",
            document -> document.body().select(".box_chart")
        );
        saveExcel("douban-us-week-movie", movies);
    }

    public static List<Movie> getMoviesByUrl(String url, Function<Document, Elements> selector) {
        // 爬取页面
        Document document = crawler.crawl(url);
        // 提取数据
        Elements items = selector.apply(document);
        List<String> textList = items.stream()
            .filter(item -> item.selectFirst("a") != null)
            .map(item -> extractor.extractUrl(item.selectFirst("a")))
            .filter(detailWebUrl -> detailWebUrl != null && !detailWebUrl.trim().isEmpty())
            .map(Main::extractDataByUrl)
            .collect(Collectors.toList());
        // 解析数据
        return textList.stream()
            .filter(text -> text != null && !text.trim().isEmpty())
            .map(text -> parser.parse(text, Main::textToMovie))
            .collect(Collectors.toList());
    }

    public static void saveExcel(String filename, List<Movie> movies) {
        XSSFWorkbook workbook = new XSSFWorkbook();

        XSSFSheet sheet = workbook.createSheet("sheet");
        // 表头
        XSSFRow head = sheet.createRow(0);
        head.createCell(0).setCellValue("电影名称");
        head.createCell(1).setCellValue("豆瓣链接");
        head.createCell(2).setCellValue("图片地址");
        head.createCell(3).setCellValue("导演");
        head.createCell(4).setCellValue("编剧");
        head.createCell(5).setCellValue("主演");
        head.createCell(6).setCellValue("类型");
        head.createCell(7).setCellValue("官方网站");
        head.createCell(8).setCellValue("制片国家/地区");
        head.createCell(9).setCellValue("语言");
        head.createCell(10).setCellValue("上映日期");
        head.createCell(11).setCellValue("片长");
        head.createCell(12).setCellValue("别名");
        head.createCell(13).setCellValue("IMDb");
        head.createCell(14).setCellValue("评分");
        head.createCell(15).setCellValue("评价人数");
        // 表数据
        int r = 1;
        for (Movie movie : movies) {
            XSSFRow row = sheet.createRow(r++);
            row.createCell(0).setCellValue(movie.getTitle());
            row.createCell(1).setCellValue(movie.getUrl());
            row.createCell(2).setCellValue(movie.getImg());
            row.createCell(3).setCellValue(movie.getDirector());
            row.createCell(4).setCellValue(movie.getScreenwriter());
            row.createCell(5).setCellValue(movie.getActor());
            row.createCell(6).setCellValue(movie.getType());
            row.createCell(7).setCellValue(movie.getWebsite());
            row.createCell(8).setCellValue(movie.getArea());
            row.createCell(9).setCellValue(movie.getLang());
            row.createCell(10).setCellValue(movie.getRelease());
            row.createCell(11).setCellValue(movie.getLength());
            row.createCell(12).setCellValue(movie.getAlias());
            row.createCell(13).setCellValue(movie.getIMDb());
            row.createCell(14).setCellValue(movie.getRating());
            row.createCell(15).setCellValue(movie.getEvaluators());
        }

        try (OutputStream fileOutput = new FileOutputStream(filename + ".xlsx")) {
            workbook.write(fileOutput);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static String extractDataByUrl(String url) {
        StringBuilder builder = new StringBuilder();
        builder.append("链接: ").append(url).append('\n');

        Document detailDocument = crawler.crawl(url);
        String data = extractor.extract(detailDocument.body(), Main::extractMovieDetailWebData);
        builder.append(data);

        return builder.toString();
    }

    private static String extractMovieDetailWebData(Element elem) {
        StringBuilder builder = new StringBuilder();

        Element h1 = elem.selectFirst("h1");
        if (h1 != null) {
            Element span = h1.selectFirst("span");
            if (span != null) {
                builder.append("标题: ").append(span.text()).append('\n');
            }
        }

        Element mainpic = elem.selectFirst("#mainpic");
        if (mainpic != null) {
            Element img = mainpic.selectFirst("img");
            if (img != null) {
                builder.append("图片: ").append(img.attr("src")).append('\n');
            }
        }

        Element info = elem.selectFirst("#info");
        if (info != null) {
            List<Node> nodes = info.childNodes();
            StringBuilder text = new StringBuilder();
            for (Node node : nodes) {
                if (node instanceof Element) {
                    Element element = (Element) node;
                    if (element.tagName().equals("br")) {
                        text.append('\n');
                    } else {
                        text.append(element.text().trim());
                    }
                } else if (node instanceof TextNode) {
                    text.append(((TextNode) node).text().trim());
                }
            }
            builder.append(text).append('\n');
        }

        Element ratingWrap = elem.selectFirst(".rating_wrap");
        if (ratingWrap != null) {
            Element ratingNum = ratingWrap.selectFirst(".rating_num");
            if (ratingNum != null) {
                builder.append("评分: ").append(ratingNum.text()).append('\n');
            }

            Element ratingPeople = ratingWrap.selectFirst(".rating_people");
            if (ratingPeople != null) {
                Element span = ratingPeople.selectFirst("span");
                if (span != null) {
                    builder.append("评价人数: ").append(span.text()).append('\n');
                }
            }
        }
        return builder.toString();
    }

    private static Movie textToMovie(String text) {
        Movie movie = new Movie();
        BufferedReader reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8))));
        try {
            String line;
            while ((line = reader.readLine()) != null) {
                if (line.trim().isEmpty()) {
                    continue;
                }

                int index;
                if ((index = line.indexOf(":")) == -1 && (index = line.indexOf("：")) == -1) {
                    continue;
                }

                String name = line.substring(0, index).trim();
                String value = line.substring(index + 1).trim();
                switch (name) {
                    case "标题":
                        movie.setTitle(value);
                        break;
                    case "链接":
                        movie.setUrl(value);
                        break;
                    case "图片":
                        movie.setImg(value);
                        break;
                    case "导演":
                        movie.setDirector(value);
                        break;
                    case "编剧":
                        movie.setScreenwriter(value);
                        break;
                    case "主演":
                        movie.setActor(value);
                        break;
                    case "类型":
                        movie.setType(value);
                        break;
                    case "官方网站":
                        movie.setWebsite(value);
                        break;
                    case "制片国家/地区":
                        movie.setArea(value);
                        break;
                    case "语言":
                        movie.setLang(value);
                        break;
                    case "上映日期":
                        movie.setRelease(value);
                        break;
                    case "片长":
                        movie.setLength(value);
                        break;
                    case "又名":
                        movie.setAlias(value);
                        break;
                    case "IMDb":
                        movie.setIMDb(value);
                        break;
                    case "评分":
                        if (!value.isEmpty()) {
                            movie.setRating(Double.parseDouble(value));
                        } else {
                            movie.setRating(0.0d);
                        }
                        break;
                    case "评价人数":
                        if (!value.isEmpty()) {
                            movie.setEvaluators(Integer.parseInt(value));
                        } else {
                            movie.setEvaluators(0);
                        }
                        break;
                }
            }
        } catch (IOException e) {
            throw new RuntimeException("读取字符串时出错", e);
        }
        return movie;
    }
}
